#!/usr/bin/env node
/**
 * Phase 5 自动校验脚本
 * 用法: node scripts/phase5-auto-validate.js ComponentName
 * 也可通过 npm run phase5 -- ComponentName 调用
 */

const { spawnSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const ts = require('typescript');

const COMPONENT_NAME = process.argv[2];

if (!COMPONENT_NAME) {
  console.error('❌ 错误: 缺少组件名称');
  console.error('用法: node scripts/phase5-auto-validate.js ComponentName');
  process.exit(1);
}

const ROOT = process.cwd();
const FIELD_FILE = path.join(ROOT, 'src/components/editor/schema/fields/aiComponents.ts');
const EDIT_FILE = path.join(ROOT, 'src/components/editor/schema/edit/aiComponents.ts');
const PREVIEW_FILE = path.join(
  ROOT,
  'src/components/editor/schema/preview/components',
  COMPONENT_NAME,
  'index.tsx',
);
const PREVIEW_REGISTRY_FILE = path.join(
  ROOT,
  'src/components/editor/schema/preview/aiCustomComponents.ts',
);
const PREVIEW_INDEX_FILE = path.join(ROOT, 'src/components/editor/schema/preview/index.ts');
const EDIT_INDEX_FILE = path.join(ROOT, 'src/components/editor/schema/edit/index.ts');
const LOCAL_TEST_FILE = path.join(ROOT, 'src/constants/constants.ts');
const RIGHT_ROOT = path.join(ROOT, 'src/components/editor/Right');

const GLOBAL_FIELD_KEYS = new Set([
  'visible',
  'groupSetting',
  'publishTime',
  'expireTime',
  'applyRules',
]);
const UNIVERSAL_PANELS = new Set(['header', 'eventReportSetting', 'groupSetting', 'publishInfo']);
const REQUIRED_MODULE_PATH = '@/components/editor/schema/preview/aiCustomComponents';
const BELONG_TO_DIR_OVERRIDES = {
  staticTextStyle: 'renderStaticTexStyle',
  renderTrendDataConfig: 'renderTrendDataConfig',
};

function runCommand(label, command, args) {
  console.log(`\n▶ ${label}`);
  const result = spawnSync(command, args, { stdio: 'inherit' });
  if (result.status !== 0) {
    throw new Error(`${label} 失败`);
  }
}

function parseSource(filePath) {
  const content = fs.readFileSync(filePath, 'utf8');
  return ts.createSourceFile(filePath, content, ts.ScriptTarget.ESNext, true, ts.ScriptKind.TS);
}

function getPropName(node) {
  if (!node) return null;
  if (ts.isIdentifier(node) || ts.isStringLiteral(node)) {
    return node.text;
  }
  return null;
}

function extractFieldInfo() {
  if (!fs.existsSync(FIELD_FILE)) {
    throw new Error(`未找到 FieldNode 文件: ${FIELD_FILE}`);
  }
  const source = parseSource(FIELD_FILE);
  let info = null;

  function visit(node) {
    if (info) return;
    if (ts.isObjectLiteralExpression(node)) {
      const typeProp = node.properties.find(
        (prop) =>
          ts.isPropertyAssignment(prop) &&
          getPropName(prop.name) === 'type' &&
          ts.isStringLiteral(prop.initializer) &&
          prop.initializer.text === COMPONENT_NAME,
      );
      if (typeProp) {
        const moduleProp = node.properties.find(
          (prop) =>
            ts.isPropertyAssignment(prop) &&
            getPropName(prop.name) === 'module' &&
            ts.isStringLiteral(prop.initializer),
        );
        const propsProp = node.properties.find(
          (prop) => ts.isPropertyAssignment(prop) && getPropName(prop.name) === 'props',
        );

        const props = [];
        if (propsProp && ts.isObjectLiteralExpression(propsProp.initializer)) {
          propsProp.initializer.properties.forEach((prop) => {
            if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
              props.push(prop.name.text);
            }
          });
        }

        info = {
          module: moduleProp && moduleProp.initializer.text,
          props,
        };
        return;
      }
    }
    ts.forEachChild(node, visit);
  }

  ts.forEachChild(source, visit);
  if (!info) {
    throw new Error(`未在 fields/aiComponents.ts 中找到 ${COMPONENT_NAME} `);
  }
  return info;
}

function extractEditItems() {
  if (!fs.existsSync(EDIT_FILE)) {
    throw new Error(`未找到 EditFields 文件: ${EDIT_FILE}`);
  }
  const source = parseSource(EDIT_FILE);
  const items = [];

  function visit(node) {
    if (ts.isPropertyAssignment(node)) {
      const name = getPropName(node.name);
      if (name === COMPONENT_NAME && ts.isArrayLiteralExpression(node.initializer)) {
        node.initializer.elements.forEach((element) => {
          if (ts.isObjectLiteralExpression(element)) {
            let key = '';
            let belongTo = '';
            element.properties.forEach((prop) => {
              if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
                if (prop.name.text === 'key' && ts.isStringLiteral(prop.initializer)) {
                  key = prop.initializer.text;
                }
                if (prop.name.text === 'belongTo' && ts.isStringLiteral(prop.initializer)) {
                  belongTo = prop.initializer.text;
                }
              }
            });
            if (key) {
              items.push({ key, belongTo });
            }
          }
        });
      }
    }
    ts.forEachChild(node, visit);
  }

  ts.forEachChild(source, visit);
  if (!items.length) {
    throw new Error(`未在 edit/aiComponents.ts 中找到 ${COMPONENT_NAME} 的配置`);
  }
  return items;
}

function readPreviewContent() {
  if (!fs.existsSync(PREVIEW_FILE)) {
    throw new Error(`未找到 Preview 组件: ${PREVIEW_FILE}`);
  }
  return fs.readFileSync(PREVIEW_FILE, 'utf8');
}

function toPascalCase(str) {
  if (!str) return '';
  return str[0].toUpperCase() + str.slice(1);
}

function checkBelongToRender(belongTo) {
  const dirName = BELONG_TO_DIR_OVERRIDES[belongTo] || `render${toPascalCase(belongTo)}`;
  const dirPath = path.join(RIGHT_ROOT, dirName);
  const indexPathTs = path.join(dirPath, 'index.tsx');
  const indexPathTsx = path.join(dirPath, 'index.ts');

  const indexPath = fs.existsSync(indexPathTs) ? indexPathTs : indexPathTsx;
  if (!indexPath || !fs.existsSync(indexPath)) {
    return { belongTo, dirPath, handled: false, reason: '缺少 index.tsx' };
  }

  const content = fs.readFileSync(indexPath, 'utf8');
  const handled = content.includes(COMPONENT_NAME);

  return {
    belongTo,
    dirPath,
    handled,
    reason: handled ? '' : `index.tsx 未包含 ${COMPONENT_NAME}`,
  };
}

function checkPreviewRegistry() {
  const registryContent = fs.readFileSync(PREVIEW_REGISTRY_FILE, 'utf8');
  const importOk = new RegExp(`import\\s+${COMPONENT_NAME}\\s+from`).test(registryContent);
  const exportSection = registryContent.split('export default')[1] || '';
  const exportOk = new RegExp(`\\b${COMPONENT_NAME}\\b`).test(exportSection);

  const indexContent = fs.readFileSync(PREVIEW_INDEX_FILE, 'utf8');
  const aggregateOk = indexContent.includes('aiCustomComponents') && indexContent.includes('...aiCustomComponents');

  return importOk && exportOk && aggregateOk;
}

function checkLocalTestRegistration() {
  const content = fs.readFileSync(LOCAL_TEST_FILE, 'utf8');
  const blockRegex = new RegExp(
    `componentCode:\\s*'${COMPONENT_NAME}'[\\s\\S]*?componentType:\\s*'AI'[\\s\\S]*?propertiesSource:\\s*'1'[\\s\\S]*?stylesSource:\\s*'1'`,
  );
  return blockRegex.test(content);
}

function checkEditAggregator() {
  const content = fs.readFileSync(EDIT_INDEX_FILE, 'utf8');
  return content.includes('aiComponents') && content.includes('...aiComponents');
}

function checkRightRenderReferences() {
  const rightDirs = fs.readdirSync(RIGHT_ROOT, { withFileTypes: true })
    .filter(dirent => dirent.isDirectory())
    .map(dirent => dirent.name);

  const referencedFields = new Set();

  rightDirs.forEach(dir => {
    const indexPath = path.join(RIGHT_ROOT, dir, 'index.tsx');
    if (!fs.existsSync(indexPath)) return;

    const content = fs.readFileSync(indexPath, 'utf8');
    if (!content.includes(COMPONENT_NAME)) return;

    // 查找对应的渲染函数文件
    const renderFile = path.join(RIGHT_ROOT, dir, `render${COMPONENT_NAME}.tsx`);
    if (fs.existsSync(renderFile)) {
      const renderContent = fs.readFileSync(renderFile, 'utf8');

      // 提取引用的字段（简单正则匹配 value.xxx 或 focusComponent.props.xxx）
      const fieldMatches = renderContent.matchAll(/(?:value|focusComponent\.props)\.(\w+)/g);
      for (const match of fieldMatches) {
        referencedFields.add(match[1]);
      }
    }
  });

  return referencedFields;
}

function runStaticChecks() {
  const issues = [];
  const warnings = [];
  const fieldInfo = extractFieldInfo();
  const editItems = extractEditItems();
  const previewContent = readPreviewContent();

  if (fieldInfo.module !== REQUIRED_MODULE_PATH) {
    issues.push(`module 应为 ${REQUIRED_MODULE_PATH}，当前为 ${fieldInfo.module || '未设置'}`);
  }

  const fieldKeys = fieldInfo.props.filter((key) => !GLOBAL_FIELD_KEYS.has(key));
  const editKeys = new Set(editItems.map((item) => item.key));
  const previewUsed = new Set();

  fieldKeys.forEach((key) => {
    const regex = new RegExp(`\\b${key}\\b`, 'g');
    if (regex.test(previewContent)) {
      previewUsed.add(key);
    }
  });

  // 检查Right面板实际引用
  const rightReferences = checkRightRenderReferences();

  // 分析EditFields配置模式
  const belongToPanels = new Set();
  const directEditFields = new Set();
  editItems.forEach(item => {
    if (item.belongTo && !UNIVERSAL_PANELS.has(item.belongTo)) {
      belongToPanels.add(item.belongTo);
    }
    if (item.key !== 'slot') {
      directEditFields.add(item.key);
    }
  });

  const editCovered = fieldKeys.filter((key) => editKeys.has(key));
  const directFieldsCoverage = fieldKeys.length ? Math.round((directEditFields.size / fieldKeys.length) * 100) : 100;
  const rightCoverage = fieldKeys.length ? Math.round((rightReferences.size / fieldKeys.length) * 100) : 100;
  const previewCoverage = fieldKeys.length ? Math.round((previewUsed.size / fieldKeys.length) * 100) : 100;

  const missingEdit = fieldKeys.filter((key) => !editKeys.has(key));
  const missingPreview = fieldKeys.filter((key) => !previewUsed.has(key));
  const missingRight = fieldKeys.filter((key) => !rightReferences.has(key) && !GLOBAL_FIELD_KEYS.has(key));

  // 改进的警告逻辑
  const usingBelongToPattern = belongToPanels.size > 0;

  if (directFieldsCoverage < 90 && !usingBelongToPattern) {
    // 只有在未使用belongTo模式且覆盖率低时才警告
    warnings.push(`Edit字段直接定义覆盖率 ${directFieldsCoverage}% < 90%，缺失字段: ${missingEdit.join(', ')}`);
  } else if (usingBelongToPattern && rightCoverage < 80) {
    // 使用belongTo模式时，检查Right面板实际引用率
    warnings.push(`Right面板实际引用覆盖率 ${rightCoverage}% < 80%，未在Right渲染函数中引用: ${missingRight.join(', ')}`);
  }

  if (previewCoverage < 80) {
    warnings.push(`Preview 覆盖率 ${previewCoverage}% < 80%，缺失字段: ${missingPreview.join(', ')}`);
  }

  const nonUniversalPanels = editItems.filter(
    (item) => item.belongTo && !UNIVERSAL_PANELS.has(item.belongTo),
  );
  const panelResults = nonUniversalPanels.map((item) => checkBelongToRender(item.belongTo));
  const missingPanels = panelResults.filter((res) => !res.handled);
  missingPanels.forEach((panel) => {
    issues.push(`渲染面板 ${panel.belongTo} 未注册 ${COMPONENT_NAME}（${panel.reason}）`);
  });

  if (!checkPreviewRegistry()) {
    issues.push('preview/aiCustomComponents.ts 缺少组件导入或导出');
  }

  if (!checkLocalTestRegistration()) {
    issues.push('LOCAL_TEST_COMPONENTS 未正确注册组件或缺少 AI/本地配置标识');
  }

  if (!checkEditAggregator()) {
    issues.push('edit/index.ts 缺少 aiComponents 聚合，无法加载自定义面板');
  }

  return {
    issues,
    warnings,
    metrics: {
      totalFields: fieldKeys.length,
      editCovered: editCovered.length,
      previewCovered: previewUsed.size,
      rightCovered: rightReferences.size,
      directFieldsCoverage,
      rightCoverage,
      previewCoverage,
      panelsChecked: nonUniversalPanels.length,
      belongToPanels: belongToPanels.size,
      usingBelongToPattern,
    },
  };
}

function main() {
  try {
    runCommand('模板一致性检查 (LOCAL_TEST_COMPONENTS)', 'node', ['scripts/lint-template-alignment.js']);
    runCommand('运行 scripts/validate-component.sh', 'bash', ['scripts/validate-component.sh', COMPONENT_NAME]);
    runCommand('生成字段引用报告', 'node', ['scripts/component-coverage-report.js', COMPONENT_NAME]);

    console.log('\n▶ Phase 5 静态链路校验');
    const { issues, warnings, metrics } = runStaticChecks();

    // 改进的输出格式
    console.log(`\n📊 覆盖率统计 (基于 ${metrics.totalFields} 个有效字段):`);
    console.log(`  • Preview 组件引用: ${metrics.previewCoverage}% (${metrics.previewCovered}/${metrics.totalFields})`);

    if (metrics.usingBelongToPattern) {
      console.log(`  • EditFields 配置模式: 面板复用模式 (belongTo)`);
      console.log(`  • 复用面板数量: ${metrics.belongToPanels} 个`);
      console.log(`  • Right 面板实际引用: ${metrics.rightCoverage}% (${metrics.rightCovered}/${metrics.totalFields})`);
      console.log(`\n💡 说明: 使用 belongTo 面板复用模式，通过 ${metrics.panelsChecked} 个自定义渲染函数实现UI配置`);
    } else {
      console.log(`  • EditFields 配置模式: 直接定义模式`);
      console.log(`  • Edit 字段定义: ${metrics.directFieldsCoverage}% (${metrics.editCovered}/${metrics.totalFields})`);
    }

    if (issues.length) {
      console.error('\n✗ Phase 5 自动验证失败:');
      issues.forEach((issue) => console.error(`  - ${issue}`));
      console.error('\n请修复上述问题后重新执行本脚本。');
      process.exit(1);
    }

    if (warnings.length) {
      console.warn('\n⚠️  Phase 5 验证警告:');
      warnings.forEach((warning) => console.warn(`  - ${warning}`));
      console.warn('\n说明: 若警告字段为公共框架字段 (profileSetting/targetLabel/needLogin/slot/publishTime/expireTime)');
      console.warn('     或已通过 belongTo 面板实现，则可忽略此警告。');
    }

    console.log('\n✓ Phase 5 自动验证完成');
    console.log('下一步：请在编辑器中手动完成拖拽、配置保存/恢复、预览模式切换等 GUI 验证。');
  } catch (error) {
    console.error(`\n✗ Phase 5 自动验证过程中断: ${error.message}`);
    process.exit(1);
  }
}

main();
